package com.jeeplus.modules.web.im;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.jeeplus.modules.web.entity.*;
import com.jeeplus.modules.web.service.WxOrderService;
import net.sf.json.JSONObject;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;

import com.jeeplus.common.utils.SpringContextHolder;
import com.jeeplus.common.utils.StringUtils;
import com.jeeplus.modules.monitor.utils.Achieve;
import com.jeeplus.modules.web.mapper.WxAgentsMapper;
import com.jeeplus.modules.web.mapper.WxOrderMapper;
import com.jeeplus.modules.web.mapper.WxUserIntegrationRecordMapper;
import com.jeeplus.modules.web.mapper.WxUserWalletMapper;
import com.jeeplus.modules.web.mapper.YjEquipmentMapper;
import com.jeeplus.modules.web.service.WxAgentsRecordingService;

@Service
public class InputThread extends Thread {
	private static Logger log = Logger.getLogger(InputThread.class);
	static int i = 0;
	static int version = 0;
	/**
	 * 此类启动早于spring的加载，注解获取实例的方式将不生效，故用SpringContextHolder手动获取实例
	 */
	private YjEquipmentMapper yjEquipmentMapper = (YjEquipmentMapper) SpringContextHolder
			.getBean(YjEquipmentMapper.class); // 获取dao 接口 防止注入失败
	private WxAgentsMapper wxAgentsMapper = (WxAgentsMapper) SpringContextHolder.getBean(WxAgentsMapper.class); // 获取dao
	// 接口
	// 防止注入失败
	private WxUserWalletMapper wxUserWalletMapper = (WxUserWalletMapper) SpringContextHolder
			.getBean(WxUserWalletMapper.class); // 获取dao 接口 防止注入失败
	private WxOrderMapper wxOrderMapper = (WxOrderMapper) SpringContextHolder.getBean(WxOrderMapper.class); // 获取dao 接口
	// 防止注入失败
	private WxUserIntegrationRecordMapper wxUserIntegrationRecordMapper = (WxUserIntegrationRecordMapper) SpringContextHolder
			.getBean(WxUserIntegrationRecordMapper.class); // 获取dao 接口 防止注入失败

	private WxAgentsRecordingService wxAgentsRecordingService = (WxAgentsRecordingService) SpringContextHolder
			.getBean(WxAgentsRecordingService.class); // 获取Service 接口 防止注入失败

	private static final String MSG_OFF = "off";// 关机回调消息

	private Socket socket;// socket对象
	private OutputThread out;// 传递进来的写消息线程，因为我们要给用户回复消息啊
	private String mac;// 写消息线程缓存器
	private InputStream ois;// 对象输入流
	boolean isStart = true;// 是否循环读消息

	public InputThread(Socket socket, OutputThread out) {
		this.socket = socket;
		this.out = out;
		try {
			i = 0;
			ois = socket.getInputStream();// 实例化对象输入流
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public void setStart(boolean isStart) {// 提供接口给外部关闭读消息线程
		this.isStart = isStart;
	}

	@Override
	public void run() {
		try {
			while (isStart) {

				// 读取消息
				// Thread.sleep(1000); // 线程休眠
				readMessage();
			}
			if (ois != null)
				ois.close();

			if (socket != null)
				socket.close();
		} catch (StringIndexOutOfBoundsException e) {
			log.debug("读读====================StringIndexOutOfBoundsException");
			log.error(e);
			log.debug(mac + ":断开了");
			e.printStackTrace();
			yjEquipmentMapper.setStop(mac, 1);// 设置设备状态为关机状态
			yjEquipmentMapper.setLightStatus(mac, -5);// 设置设备开关灯状态 -5为关灯状态
			if (ois != null)
				try {
					out.setStart(false);
					setStart(false);

					ois.close();
					if (socket != null)
						socket.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
		} catch (Exception e) {
			e.printStackTrace();
			yjEquipmentMapper.setStop(mac, 1);
			yjEquipmentMapper.setLightStatus(mac, -5);// 设置设备开关灯状态 -5为关灯状态
			log.debug("读读====================ClassNotFoundException");
			log.debug(mac + ":断开了");
			log.error(e);
			log.error("未知的错误");
			out.setStart(false);
			setStart(false);
			try {
				socket.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}

		}

	}

	static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");

	public void readMessage() throws IOException, ClassNotFoundException {
		// run in a second
		int length = 0;
		byte[] buf = new byte[10240];
		length = ois.read(buf);
		String str = new String(buf, 0, length);
		// 关机回调消息：
		// {"msg":"success","code":"0","data":{"orderNumber":["7eba14da5c6d46ab859cf0ccbe234813"],"type":"1"}}
		// 初始建立连接消息：{"msg":"success","code":"0","data":"","equipment":"233"}
		// 开机回调消息：{"msg":"success","code":"0","data":{"orderNumber":["7eba14da5c6d46ab859cf0ccbe234813"],"type":"0"}}
		// 还剩五分钟消息：{"msg":"success","code":"0","data":{"type":"2","msg":"5Minoff"},"equipment":"233"}
		// 已关机消息：off
		// 开灯回调消息：{"msg":"success","code":"0","data":{"orderNumber":["7eba14da5c6d46ab859cf0ccbe234813"],"type":"5"}}
		// 关灯回调消息：{"msg":"success","code":"0","data":{"orderNumber":["7eba14da5c6d46ab859cf0ccbe234813"],"type":"-5"}}
		log.debug("读头返信息:" + str + ",time:" + dateFormat.format(System.currentTimeMillis()));
		// 关机回调消息为“off”，此处判断，避免json解析出错
		if (!StringUtils.equalsIgnoreCase(str, MSG_OFF)) {
			// JSONObject jsonObject = JSONObject.fromObject(str);

			EquipmentIoPkg pkg = null;
			try {
				pkg = com.alibaba.fastjson.JSONObject.parseObject(str.replace("\"data\":\"\"", ""),
						EquipmentIoPkg.class);
			} catch (Exception e) {
				if (e instanceof com.alibaba.fastjson.JSONException) {
					try {
						pkg = com.alibaba.fastjson.JSONObject.parseObject(str.replace("}}", "}"), EquipmentIoPkg.class);
					} catch (Exception e2) {
						log.debug("解析设备消息异常");
					}
				}
			}

			// 第一次mac地址
			EquipmentIoPkg.PkgDate pkgDate = pkg.getData();
			try {
				/*
				 * 只有初始建立连接消息和还剩五分钟倒计时有equipment属性
				 */
				if (StringUtils.isNotEmpty(pkg.getEquipment())) {
					this.mac = pkg.getEquipment();
					out.setMac(this.mac);
					if (pkgDate != null) {
						if (pkgDate.getType() == 2) {
							orderStop(str);
						}
					} else {
						yjEquipmentMapper.setStop(mac, 0);// 设置设备状态为开机状态
					}
					//
					wxOrderMapper.setVersion(mac);
					Integer is = wxOrderMapper.getVersion(mac);
					version = is == null ? 0 : is;
					out.setVersion(version);
					log.debug("=======开机请求成功，设备号：" + pkg.getEquipment());

				} else {

					/*
					 * type为空，或type为0，开机回调消息
					 */
					if (pkgDate.getType() == null || (pkgDate.getType() == 0)) {
						// code为0，操作成功，其他则为异常情况
						if (pkg.getCode() != 0) {
							wxOrderMapper.setOrder(pkgDate.getOrderNumber(), "", 4, str, "");
						} else {
							if (StringUtils.isEmpty(pkgDate.getOrderNumber())) {
								return;
							}
							Integer orderTime = wxOrderMapper.getOrderTime(pkgDate.getOrderNumber());
							orderTime = orderTime == null ? 60 : orderTime;
//							yjEquipmentMapper.setTotal(this.mac, orderTime);// 更新yj_equipment表的同时，set_time字段会更新，作为机器最近的心跳响应时间
							yjEquipmentMapper.addRecording(pkgDate.getOrderNumber(), this.mac, orderTime);
							wxOrderMapper.setOrder(pkgDate.getOrderNumber(), "", 2, "", "");
							// 命令已经处理
							yjEquipmentMapper.setType(this.mac);

						}
						log.debug("=======开机回调成功，设备号：" + pkg.getEquipment());

					} else if (pkgDate.getType() == 1) {
						// type为1，为关机回调消息
						WxAgents wxAgents = yjEquipmentMapper.getAgents(mac);// 此处为了获取设备的开机单价
                        //查出此设备关联的订单，全部设置成已完成。
                        List<WxOrder> orderStop = wxOrderMapper.getOrderByStatus(mac,2);
                        for (WxOrder wxOrder : orderStop) {
							String number = wxOrder.getOrderNumber();
							// 如果wxOrder不为null，且状态为4，则认为已经处理过，不再处理
							// 此判断是容错处理，硬件端可能重复发送相同订单type=1（关机回调信息），导致重复执行下面的关机逻辑
							if (null != wxOrder && StringUtils.equalsIgnoreCase(wxOrder.getStatus(), "4")) {
								log.info(String.format("订单%s状态为4，已经处理过，不再处理.", number));
								continue;
							}
							// 如果code=0，是机器定单到期关机回调信息，如果code为空字符串或其他，则是用户主动关机的回调信息
							// if(!jsonObject.get("code").equals("0")) {
							// log.info("type=1,code=" + jsonObject.get("code") + "的分支,收到的是用户请求或异常关机回调命令.");
							wxOrderMapper.setOrder(number, "", 4, str, "");
							double moneyShouldReturned = 0.0;// 退款金额
							long l = System.currentTimeMillis();
							// 中途关机则退款
							// 只有余额开机才会涉及到退款
							if (wxOrder.getEndTime().getTime() > l && wxOrder.getType().equals("1")) {
								long time = wxOrder.getEndTime().getTime() - l;// 计算应该退款的时间长度
								log.info(String.format("订单%s剩余时长:%s", number, time));
								// 剩余时长大于一小时，则进行退款处理
								int remainedHours = 0;
								if (time > (1000L * 60 * 60)) {
									remainedHours = (int) (time / 1000L / 60 / 60);// 剩余时长转换成小时
									Double money = 0.0;// 应该退款的金额
									// 应退款额=剩余时间*开机单价。如果通过mac能获取到开机单价，则用单价，否则用默认单价2.0
									if (null != wxAgents && wxAgents.getMoney() != null && wxAgents.getMoney() > 0.0) {
										money = (double) (time / 1000L / 60 / 60) * wxAgents.getMoney();
									} else {
										money = (double) (time / 1000L / 60 / 60) * 2.0;
									}
									// 使用优惠券的情况下，最多只退订单金额的钱
									// 常见场景：开机单价2元，现下单开机100元，使用9元，即订单金额91元，此时订单时长50小时，如开机不到一小时即关机，
									// 那么，如果不加此逻辑，最终会退给用户的金额为：49（剩余时长）*2（单价）= 98，比下单金额91元多出7元
									moneyShouldReturned = money >= Double.parseDouble(wxOrder.getOrderMoney())
											? Double.parseDouble(wxOrder.getOrderMoney())
											: money;
									if (money > 0) {
										log.info("进入退款和扣减积分逻辑");
										wxUserWalletMapper.setAddMoney(String.valueOf(money), String.valueOf(money),
												number);
										// 扣减积分
										wxUserWalletMapper.setIntegration(wxOrder.getUser().getId(), money.intValue());
										// 增加积分明细
										WxUserIntegrationRecord wxUserIntegrationRecords = new WxUserIntegrationRecord();
										WxUser user = new WxUser();
										user.setId(wxOrder.getUser().getId());
										wxUserIntegrationRecords.setUser(user);
										wxUserIntegrationRecords.setType(0);
										wxUserIntegrationRecords.setText("退款扣减");
										// 积分扣减
										wxUserIntegrationRecords.setIntegration(String.valueOf(-money.intValue()));
										wxUserIntegrationRecordMapper.insert(wxUserIntegrationRecords);
										log.info("完成退款和扣减积分逻辑");
									}

									String orderNumber = Achieve.getUUIDString();
									log.info("开始订单剩余时长退款 InputThread.readMessage");
									wxOrderMapper.addOrder(orderNumber, wxOrder.getUser().getId(),
											number + "订单剩余时长退款 InputThread.readMessage", "4",
											number + String.format("订单剩余时长(小时):%s", remainedHours),
											String.valueOf(money), "0", null, 0,
											new Date(),new Timestamp(System.currentTimeMillis()), 0.0, "0");
									log.info("结束订单剩余时长退款 InputThread.readMessage");
									wxOrderMapper.setOrder(orderNumber, null, 4, str, "");
								}

							}
							// 查询定单金额
							// Double orderMoney=wxOrderMapper.getOrderMoney(number);
							// 之前的逻辑有点问题，退款情况下，退款那部分金额应该无返佣
							// 此处应该用上面退款逻辑里产生的退款定单ID查询，获取orderMoney，此处直接用原定单金额-moneyShouldReturned，效果一样
							Double orderMoney = Double.parseDouble(wxOrder.getOrderMoney()) - moneyShouldReturned;
							Double money = orderMoney <= 0.0 ? 0.0 : orderMoney;
							// 如果orderMoney不为null的话，money和orderMoney是相等的
							log.info("开始进入InputThread.saveAgentRecordingForAgents方法.");
							saveAgentRecordingForAgents(number, orderMoney, money);
							// }else{
							// log.info("type=1,code=0的分支,收到的是正常关机回调命令.");
							// wxOrderMapper.setOrder(number, "", 3,"","");
							// log.info("开始进入InputThread.saveAgentRecordingForAgents方法.");
							// saveAgentRecordingForAgents(number, Double.valueOf(wxOrder.getOrderMoney()),
							// Double.valueOf(wxOrder.getOrderMoney()));
							// }

						}
						// 命令已经处理
						yjEquipmentMapper.setType(this.mac);

					} else if (pkgDate.getType() == 5) {
						// code为0，操作成功，其他则为异常情况
						if (pkg.getCode() != 0) {
							wxOrderMapper.setOrder(pkgDate.getOrderNumber(), "", 4, str, "");
						} else {
							if (StringUtils.isEmpty(pkgDate.getOrderNumber())) {
								return;
							}
							wxOrderMapper.setOrder(pkgDate.getOrderNumber(), "", 4, str, "");// 标记订单状态
							yjEquipmentMapper.setLightStatus(mac, 5);// 设置设备开关灯状态
							// 通过orderNumber匹配命令表，标记已经处理的命令，如果用mac，可能与开机命令混淆
							yjEquipmentMapper.setTypeByOrderNumber(pkgDate.getOrderNumber());
						}
					} else if (pkgDate.getType() == -5) {
						// code为0，操作成功，其他则为异常情况
						if (pkg.getCode() != 0) {
							wxOrderMapper.setOrder(pkgDate.getOrderNumber(), "", 4, str, "");
						} else {
							if (StringUtils.isEmpty(pkgDate.getOrderNumber())) {
								return;
							}
							wxOrderMapper.setOrder(pkgDate.getOrderNumber(), "", 4, str, "");// 标记订单状态
							yjEquipmentMapper.setLightStatus(mac, -5);// 设置设备开关灯状态,-5为关灯状态
							// 通过orderNumber匹配命令表，标记已经处理的命令，如果用mac，可能与开机命令混淆
							yjEquipmentMapper.setTypeByOrderNumber(pkgDate.getOrderNumber());
						}
					} else {
						// 疑问：目前约定的type只有0开机，1关机，5开灯，-5关灯，这个分支具体指收到什么响应？
						// 其他类型的消息判定为错误消息，不处理
						orderStop(str);
					}

				}

			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {

		}

	}

	private void orderStop(String msg) {
		List<WxOrder> wxOrders = wxOrderMapper.getOrderByStatus(mac,2);
		yjEquipmentMapper.setStop(mac, 1);// 设置设备开关机状态
		yjEquipmentMapper.setLightStatus(mac, -5);// 设置设备开关灯状态
		WxAgents wxAgents = yjEquipmentMapper.getAgents(mac);
		for (WxOrder wxOrder : wxOrders) {
			String number = wxOrder.getOrderNumber();

			wxOrderMapper.setOrder(number, "", 3, "机器关闭,强制结束", msg);
			double moneyShouldReturned = 0.0;
			long l = System.currentTimeMillis();
			// 中途关机则退款
			if (wxOrder.getEndTime().getTime() > l && wxOrder.getType().equals("1")) {
				long time = wxOrder.getEndTime().getTime() - l;
				log.info(String.format("订单%s剩余时长:%s", number, time));
				// 剩余时长大于一小时，则进行退款处理
				int remainedHours = 0;
				if (time > (1000L * 60 * 60)) {
					remainedHours = (int) (time / 1000L / 60 / 60);// 剩余时长转换成小时
					Double money = 0.0;// 应该退款的金额
					// 应退款额=剩余时间*开机单价。如果通过mac能获取到开机单价，则用单价，否则用默认单价2.0
					if (null != wxAgents && wxAgents.getMoney() != null && wxAgents.getMoney() > 0.0) {
						money = (double) (time / 1000L / 60 / 60) * wxAgents.getMoney();
					} else {
						money = (double) (time / 1000L / 60 / 60) * 2.0;
					}
					moneyShouldReturned = money;
					// 退款需要扣减积分
					if (money > 0) {
						log.info("进入退款和扣减积分逻辑");
						wxUserWalletMapper.setAddMoney(String.valueOf(money), String.valueOf(money), number);
						// 扣减积分
						wxUserWalletMapper.setIntegration(wxOrder.getUser().getId(), money.intValue());
						// 增加积分明细
						WxUserIntegrationRecord wxUserIntegrationRecords = new WxUserIntegrationRecord();
						WxUser user = new WxUser();
						user.setId(wxOrder.getUser().getId());
						wxUserIntegrationRecords.setUser(user);
						wxUserIntegrationRecords.setType(0);
						wxUserIntegrationRecords.setText("退款扣减");
						wxUserIntegrationRecords.setIntegration(String.valueOf(-money.intValue()));
						wxUserIntegrationRecordMapper.insert(wxUserIntegrationRecords);
						log.info("完成退款和扣减积分逻辑");
					}

					String orderNumber = Achieve.getUUIDString();
					log.info("开始订单剩余时长退款 InputThread.orderStop");
					wxOrderMapper.addOrder(orderNumber, wxOrder.getUser().getId(),
							number + "订单剩余时长退款 InputThread.orderStop", "4",
							number + String.format("订单剩余时长(小时):%s", remainedHours), String.valueOf(money), "0", null, 0,
							new Date(),new Timestamp(System.currentTimeMillis()), 0.0, "0");
					log.info("结束订单剩余时长退款 InputThread.orderStop");
					wxOrderMapper.setOrder(orderNumber, null, 4, msg, "");

				}

			}
			// Double orderMoney=wxOrderMapper.getOrderMoney(number);
			// 之前的逻辑有点问题，退款情况下，退款那部分金额应该无返佣
			// 此处应该用上面退款逻辑里产生的退款定单ID查询，获取orderMoney，此处直接用原定单金额-moneyShouldReturned，效果一样
			Double orderMoney = Double.parseDouble(wxOrder.getOrderMoney()) - moneyShouldReturned;
			Double money = orderMoney <= 0.0 ? 0 : orderMoney;
			// 为代理商增加分佣和分佣记录
			// 如果orderMoney不为null的话，money和orderMoney是相等的
			log.info("开始进入InputThread.saveAgentRecordingForAgents方法.");
			saveAgentRecordingForAgents(number, orderMoney, money);

			/*
			 * if(wxAgents!=null){
			 *
			 * //money=Achieve.getPrice((Double.valueOf(wxOrder.getOrderMoney())/100)*
			 * wxAgents.getProportion());
			 * money=Achieve.getPrice((money/100)*wxAgents.getProportion());
			 * money=money==0?0.01:money;
			 * yjEquipmentMapper.setAgents(money,number,wxAgents.getId()); WxAgentsRecording
			 * wxAgentsRecording=new WxAgentsRecording();
			 * wxAgentsRecording.setAgents(wxAgents); wxAgentsRecording.setMoney(money);
			 * wxAgentsRecording.setOrderMoney(orderMoney);
			 * wxAgentsRecording.setNumber(number); wxAgentsRecording.setEquipment(mac);
			 * wxAgentsRecordingMapper.insert(wxAgentsRecording); }
			 */
		}
	}

	/**
	 * 为代理商增加分佣和分佣记录
	 *
	 * @param number
	 * @param orderMoney
	 * @param money
	 */
	private void saveAgentRecordingForAgents(String number, Double orderMoney, Double money) {
		WxAgents wxAgents = yjEquipmentMapper.getAgents(mac);

		// 分佣计算方式：当前代理商及其所有父级代理商都需要进行分佣，
		// 如A总代理B一级代理下的C代理商所经营的机器盈利，此时结算分佣，ABC都将有相应的分佣

		// 最顶层代理商的parent_id为-1，当到达最顶层之后，
		while (wxAgents != null) {
			WxAgentsRecording recording = wxAgentsRecordingService.getRecordingByAgentIdAndOrderNumber(wxAgents.getId(),
					number);
			// 记录分佣之前，先判断代理商是否已经获取该订单的分佣，避免硬件发送多次响应消息时，重复记录分佣数据
			if (null != recording) {
				log.info(String.format("ID为%s的代理商已存在订单号为%s的分佣记录", wxAgents.getId(), number));
			} else {
				// 保存当前代理商的分佣
				try {
					log.info(String.format("为ID为%s的代理商增加分佣数据", wxAgents.getId()));
					wxAgentsRecordingService.saveWxAgentsRecording(wxAgents, money, orderMoney, number, mac,0);
				} catch (Exception e) {
					log.error("增加分佣数据失败，代理商ID为：" + wxAgents.getId());
					log.error("异常消息：" + e.getMessage());
					e.printStackTrace();
				}
			}

			// 通过parentId查询父级代理商，如果parentId为null，则转换为null。如果直接调用String.valueOf(wxAgents.getParentId())，转换成的是"null"字符串
			// 如果当前agent已经是顶层，那么将查不到agent数据，循环终止
			// 当代理商层级数据不完整时，此方法亦可行
			String parentId = wxAgents.getParentId() == null ? null : String.valueOf(wxAgents.getParentId());
			wxAgents = wxAgentsMapper.get(parentId);
		}
	}
}
